﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace MapAndCharacter_Filip
{
    struct MapMessage
    {
        public int userID;
        public int x;
        public int y;

        public MapMessage(int userID, int x, int y)
        {
            this.userID = userID;
            this.x = x;
            this.y = y;
        }
    }

    struct ChatMessage
    {
        public string messageString;
        public int userID;
        public bool valid;

        public ChatMessage(string message, bool valid, int userID)
        {
            this.messageString = message;
            this.valid = valid;
            this.userID = userID;
        }
    }

    static class NetworkManager
    {
        const int MAPID = 1;
        const int CHATID = 2;
        const int MAPREQUEST = 3;
        const int MAPRECEIVED = 4;
        const int CHARRECEIVED = 5;
        static private Queue<MapMessage> mapQueue;
        static private Queue<ChatMessage> chatQueue;
        public const int port = 13000;

        static NetworkManager()
        {
            mapQueue = new Queue<MapMessage>();
            chatQueue = new Queue<ChatMessage>();
        }

        static public byte[] IntToBytes(int[] array)
        {
            byte[] bytes = new byte[array.Length * sizeof(int)];
            Buffer.BlockCopy(array, 0, bytes, 0, bytes.Length);
            return bytes;
        }

        static public int[] BytesToInt(byte[] bytes)
        {
            int[] array = new int[bytes.Length / sizeof(int)];
            Buffer.BlockCopy(bytes, 0, array, 0, bytes.Length);
            return array;
        }

        static public byte[] StringToBytes(string s)
        {
            Byte[] bytes = System.Text.Encoding.ASCII.GetBytes(s);
            return bytes;
        }

        static public string BytesToString(byte[] bytes)
        {
            string s = System.Text.Encoding.ASCII.GetString(bytes, 0, bytes.Length);
            return s;
        }

        public static bool SendMapMessage(int x, int y, int ID)
        {
            int[] array = new int[4];
            array[0] = MAPID;
            array[1] = ID;
            array[2] = x;
            array[3] = y;
            GameState.client.Send(IntToBytes(array));
            return true;
        }

        public static bool SendChatMessage(string message, int ID)
        {
            int[] array = new int[3];
            array[0] = CHATID;
            array[1] = ID;
            array[2] = message.Length;
            GameState.client.Send(IntToBytes(array));
            GameState.client.Send(StringToBytes(message));
            return true;
        }

        public static void SendMap(string mapName)
        {
            if (!GameState.IsHost)
                return;

            int[] array = new int[1];
            array[0] = MAPRECEIVED;
            HostThread.Forward(IntToBytes(array), GameState.client.ID);

            Byte[] fileBytes = File.ReadAllBytes(mapName);
            array[0] = fileBytes.Length;
            HostThread.Forward(IntToBytes(array), GameState.client.ID);
            byte[] fileSize = new byte[sizeof(int)];
            Buffer.BlockCopy(array, 0, fileSize, 0, sizeof(int));

            HostThread.Forward(fileBytes, GameState.client.ID);
            Console.WriteLine("bytes sent: " + fileBytes.Length);
        }

        public static void SendCharacter(string charName, int ID = -1)
        {
            Byte[] fileBytes = File.ReadAllBytes(charName);
            int[] array = new int[3];
            array[0] = CHARRECEIVED;
            if (ID == -1)
                array[1] = GameState.client.ID;
            else
                array[1] = ID;
            array[2] = fileBytes.Length;

            if (GameState.IsHost)
            {
                HostThread.Forward(IntToBytes(array), GameState.client.ID);
                HostThread.Forward(fileBytes, GameState.client.ID);
            }
            else
            {
                GameState.client.Send(IntToBytes(array));
                GameState.client.Send(fileBytes);
            }
        }

        public static ChatMessage GetChatMessage()
        {
            if (chatQueue.Count > 0)
            {
                Console.WriteLine("Getting: " + chatQueue.First().messageString);
                return chatQueue.Dequeue();
            }
            else
                return (new ChatMessage("", false, -1));
        }

        public static MapMessage GetMapMessage()
        {
            if (mapQueue.Count > 0)
                return mapQueue.Dequeue();
            else
                return (new MapMessage(-1, -1, -1));
        }

        public static void ReadChatMessage(NetworkStream stream)
        {
            Byte[] bytes = new Byte[sizeof(int) * 2];
            stream.Read(bytes, 0, bytes.Length);
            int[] array = BytesToInt(bytes);
            bytes = new byte[array[1]];
            stream.Read(bytes, 0, bytes.Length);
            string message = BytesToString(bytes);

            Console.WriteLine("Received message from id " + array[0] + ": " + message);

            if (array[0] != GameState.client.ID)
            {
                ChatMessage newMessage = new ChatMessage(message, true, array[1]);
                chatQueue.Enqueue(newMessage);
            }

            if (GameState.IsHost)
            {
                int[] forwardingArray = new int[3];
                forwardingArray[0] = CHATID;
                forwardingArray[1] = array[0];
                forwardingArray[2] = array[1];
                HostThread.Forward(IntToBytes(forwardingArray), array[0]);
                HostThread.Forward(StringToBytes(message), array[0]);
            }
        }

        public static void ReadMapMessage(NetworkStream stream)
        {
            byte[] array = new byte[sizeof(int) * 3];
            stream.Read(array, 0, array.Length);
            int[] coords = BytesToInt(array);

            MapMessage newMessage = new MapMessage(coords[0], coords[1], coords[2]);
            mapQueue.Enqueue(newMessage);
            Console.WriteLine("Received map message, id = " + coords[0] +
                " coords= x:" + coords[1] + " y:" + coords[1]);

            if (GameState.IsHost)
            {
                int[] forwardingArray = new int[4];
                forwardingArray[0] = MAPID;
                forwardingArray[1] = coords[0];
                forwardingArray[2] = coords[1];
                forwardingArray[3] = coords[2];

                HostThread.Forward(IntToBytes(forwardingArray), coords[0], 1);
            }
        }

        public static void ReceiveCharacter(NetworkStream stream)
        {
            Thread.Sleep(10);
            byte[] data = new byte[sizeof(int) * 2];
            int i = stream.Read(data, 0, data.Length);
            int[] array = BytesToInt(data);

            byte[] fileBytes = new byte[array[1]];
            int bytesRead = 0;
            while (bytesRead < fileBytes.Length && i != 0)
            {
                int bytesToRead = fileBytes.Length / 4;
                if (fileBytes.Length - bytesRead < bytesToRead)
                    bytesToRead = fileBytes.Length - bytesRead;
                i = stream.Read(fileBytes, bytesRead, bytesToRead);
                bytesRead += i;
            }
            string filename = "char" + array[0] + ".jpg";
            File.WriteAllBytes(filename, fileBytes);

            using (FileStream fileStream = new FileStream(filename, FileMode.Open))
            {
                if (!GameState.game.characters.ContainsKey(array[0]))
                {
                    MovableSprite character = new MovableSprite(GameState.game.Content, GameState.game.spriteBatch);
                    character.Load("TempCharacter");
                    character.Position = new Vector2(20, 20);
                    character.Color = Color.White;
                    GameState.game.characters.Add(array[0], character);
                }
                GameState.game.characters[array[0]]
                    .SetImage(Texture2D.FromStream(GameState.game.GraphicsDevice, fileStream));
                GameState.game.characters[array[0]]
                    .Color = Color.White;
            }
        }

        public static void ReceiveMap(NetworkStream stream)
        {
            Thread.Sleep(10);
            byte[] sizeInBytes = new byte[sizeof(int)];
            int i = stream.Read(sizeInBytes, 0, sizeof(int));
            int[] size = BytesToInt(sizeInBytes);

            int bytesRead = 0;
            Console.WriteLine("Expecting #bytes: " + size[0]);
            byte[] fileBytes = new byte[size[0]];

            while (bytesRead < fileBytes.Length && i != 0)
            {
                int bytesToRead = fileBytes.Length/4;
                if(fileBytes.Length-bytesRead < bytesToRead)
                    bytesToRead = fileBytes.Length - bytesRead;
                i = stream.Read(fileBytes, bytesRead, bytesToRead);
                bytesRead += i;
            }
            Console.WriteLine("bytes read: " + bytesRead);

            File.WriteAllBytes("map.jpg", fileBytes);
            GameState.mapImage = Directory.GetCurrentDirectory() + "\\map.jpg";
            GameState.IsNewMap = true;
        }
    }
}
